/*
 * Copyright (c) 2024 iSOFT INFRASTRUCTURE SOFTWARE CO., LTD.
 * easyAda is licensed under Mulan PubL v2.
 * You can use this software according to the terms and conditions of the Mulan PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *          http://license.coscl.org.cn/MulanPubL-2.0
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
 */

#ifndef MACROS_H
#define MACROS_H

#include <stdlib/stdint.h>
#include <stdlib/assert.h>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#pragma PRQA_MACRO_MESSAGES_OFF "container_of" 0306, 2810
#pragma PRQA_MACRO_MESSAGES_OFF "offset_of" 0306
#pragma GCC diagnostic pop

#define offset_of(t, m)               ((unsigned long)&((t *)0)->m)
#define container_of(p, type, member) ((type *)((unsigned long)(p) - (unsigned long)(&((type *)0)->member)))

#define likely(x)   (x)
#define unlikely(x) (x)

#ifndef bitmask
#define bitmask(x) (1UL << (x))
#endif

#ifndef lowbitsmask
#define lowbitsmask(x) (bitmask(x) - 1UL)
#endif

#define align_to(size, align) ((((size) + (align)-1) / (align)) * (align))

#define GET_BIT(x, bit)   (((x) & (1ULL << (bit))) >> (bit))
#define SET_BIT(x, bit)   ((x) |= (1ULL << (bit)))
#define CLEAR_BIT(x, bit) ((x) &= ~(1ULL << (bit)))

#define TO_LOWER(c) (unsigned char)((c >= 'A' && c <= 'Z') ? (c | 0x20) : c)
#define TO_UPPER(c) (unsigned char)((c >= 'a' && c <= 'z') ? (c & ~0x20) : c)
#define TO_LOWER_STR(s, len)                            \
    {                                                   \
        for (int i = 0; i < len && s[i] != '\0'; i++) { \
            s[i] = TO_LOWER(s[i]);                      \
        }                                               \
    }
#define TO_UPPER_STR(s, len)                            \
    {                                                   \
        for (int i = 0; i < len && s[i] != '\0'; i++) { \
            s[i] = TO_UPPER(s[i]);                      \
        }                                               \
    }

#define WEAK __attribute__((weak))

#ifndef ALIGNED
#define ALIGNED(x) __attribute__((aligned(x)))
#endif
#define DEFAULT_ALINMENT ALIGNED(8)

#define REG64_RW(addr) (*(volatile uint64_t *)(addr))
#define REG64_RO(addr) (*(const volatile unsigned long *)(addr))

#define REG32_RW(addr) (*(volatile uint32_t *)(addr))
#define REG32_RO(addr) (*(const volatile unsigned int *)(addr))

#define REG64_WO(addr)  REG64_RW(addr)
#define REG64s_RW(addr) ((volatile uint64_t *)(addr))
#define REG64s_RO(addr) ((const volatile uint64_t *)(addr))
#define REG64s_WO(addr) REG64s_RW(addr)

#define BOOTONLY   __attribute__((section(".boot")))
#define BOOTDATA   __attribute__((section(".boot.data")))
#define BOOTPHYSIC __attribute__((section(".boot.physic")))

#define WEAK __attribute__((weak))

#define ROUND_UP(x, n)   (((x) + (n)-1) & ~((n)-1))
#define ROUND_DOWN(x, n) ((x) & ~((n)-1))

static inline unsigned long bitfield_get(unsigned long value, unsigned int begin, unsigned int width)
{
    assert(width > 0 && begin < (sizeof(unsigned long) * 8 - 1));
    return (value >> begin) & lowbitsmask(width);
}

static inline unsigned long bitfield_clean(unsigned long value, unsigned int begin, unsigned int width)
{
    assert(width > 0 && begin < (sizeof(unsigned long) * 8 - 1));
    value &= ~(lowbitsmask(width) << begin);
    return value;
}

static inline unsigned long bitfield_set(unsigned long value, unsigned int begin, unsigned int width, unsigned int bv)
{
    assert(bv < lowbitsmask(width));
    value = bitfield_clean(value, begin, width);
    value |= bv << begin;
    return value;
}

static inline unsigned long LOG(unsigned long data)
{
    unsigned long num;
    asm volatile("clz %0,%1" : "=r"(num) : "r"(data));
    return 64UL - num - 1UL;
}

static bool is_power_of_2(unsigned long n)
{
    return ((n != 0UL) && ((n & (n - 1UL)) == 0UL));
}

#endif
